home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / lib / table / tb_ns.4.2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-28  |  17.9 KB  |  899 lines

  1. /*
  2.  *      TB_NS.C
  3.  *
  4.  * Original version by Phil Cockcroft to use MF and MD records.
  5.  * Also had several speed improvements such as caching.  Later
  6.  * reworked by Phil to do MX lookups.
  7.  *
  8.  * Mostly rewritten by Craig Partridge to move routing decisions
  9.  * into SMTP channel.
  10.  *
  11.  */
  12.  
  13. #include "util.h"
  14. #include "mmdf.h"
  15. #include "ch.h"
  16. #include "ns.h"
  17. #include <netdb.h>
  18. #include <sys/socket.h>
  19. #include <netinet/in.h>
  20. #include <arpa/nameser.h>
  21. #include <resolv.h>
  22.  
  23. extern char *strncpy();
  24. extern int   h_errno;
  25.  
  26. /* T_UNSPEC was defined only in more recent versions of BIND */
  27.  
  28. #ifdef T_UNSPEC
  29. #define BSD4_3
  30. #define    getshort _getshort
  31. #endif T_UNSPEC
  32.  
  33. /*
  34.  * there turn out to be some servers that return FORMERR to MX queries!?
  35.  */
  36.  
  37. #define ROBUST
  38.  
  39. /*
  40.  * if you want caching, define NSCACHE.  Make it a prime number!
  41.  */
  42.  
  43. #define NSCACHE 101
  44.  
  45. /*
  46.  * next definition should go away as all servers use MX
  47.  * right now if they don't we just try the address.  MF and MD are
  48.  * dead!
  49.  */
  50.  
  51. #define OLDSERVER
  52.  
  53. #ifndef MAXADDR
  54. #define MAXADDR        10
  55. #endif
  56.  
  57. #ifndef MAXADDR_PER
  58. #define MAXADDR_PER    2
  59. #endif
  60.  
  61. #ifndef MAXDATA
  62. #define MAXDATA (4 * PACKETSZ)        /* tcp tried after udp */
  63. #endif
  64.  
  65. #ifndef MAXMX
  66. #define MAXMX    (MAXADDR)        /* shouldn't be < MAXADDR */
  67. #endif
  68.  
  69. extern  struct  ll_struct *logptr;
  70. extern  char    *strdup(), *strcpy();
  71. extern  char *locfullmachine, *locfullname;
  72.  
  73. union ansbuf {            /* potentially huge */
  74.     HEADER ab1;
  75.     char ab2[MAXDATA];
  76. }; 
  77.  
  78. union querybuf {        /* just for outbound stuff */
  79.     HEADER qb1;            /* didn't want to clobber stack */
  80.     char qb2[2 * MAXDNAME];
  81. }; 
  82.  
  83. LOCVAR  struct in_addr mx_addrs[MAXADDR];    /* cache of MX addrs */
  84. LOCVAR  int max_mxa= -1, on_mxa= -1;    /* indicies into cache of MX addrs */
  85. LOCVAR  char dn_name[MAXDNAME];
  86. LOCVAR  char local[MAXDNAME];
  87.  
  88. #ifdef NSCACHE
  89. struct ns_cache {
  90.     int nc_rval;        /* OK/NOTOK/MAYBE */
  91.     int nc_count;
  92.     char *nc_key;
  93.     char *nc_data;
  94.     struct ns_cache *nc_next;
  95. };
  96.  
  97. LOCVAR  struct ns_cache *dmncache[NSCACHE];
  98. LOCVAR  struct ns_cache *chncache[NSCACHE];
  99. #endif
  100.  
  101. /*
  102.  * table fetch routine for ns
  103.  */
  104.  
  105. ns_fetch(table, key, value, first)
  106. Table   *table;          /* What "table" are we searching */
  107. char    *key;
  108. char    *value;         /* Where to put result */
  109. int     first;          /* now used */
  110. {
  111.     register char *tmp;
  112.     int    type;
  113.     int rval;
  114.  
  115. #ifdef  DEBUG
  116.     ll_log(logptr, LLOGFTR, "ns_fetch (%o, %s, %d)",
  117.     table->tb_flags, key, first);
  118.     ll_log(logptr, LLOGFTR, "ns_fetch: timeout (%d), rep (%d), servers (%d)",
  119.     _res.retrans, _res.retry, _res.nscount);
  120. #endif
  121.  
  122.     /* _res.options |= ((int) RES_DEBUG); /* In case it's needed again -- DSH */
  123.  
  124.     if (value == 0)
  125.     {
  126.     ll_log(logptr,LLOGFAT,"null buffer passed to ns_fetch()");
  127.     return(NOTOK);
  128.     }
  129.  
  130.     /* so we don't return garbage on errors */
  131.     *value = '\0';
  132.  
  133.     type = (table->tb_flags & TB_TYPE);
  134.  
  135.     if (first)
  136.     {
  137.     max_mxa = on_mxa = -1;
  138.  
  139.     if (!cachehit(key,&rval,type))
  140.     {
  141.         if (type == TB_CHANNEL)
  142.         {
  143.         if ((rval = ns_getmx(key, &max_mxa, mx_addrs, MAXADDR))==OK)
  144.             on_mxa = 0;
  145.  
  146. #ifdef NSCACHE
  147.         cachechn(key,rval,max_mxa,mx_addrs);
  148. #endif
  149.         }    
  150.         else
  151.         {
  152.         rval = ns_getcn(key, dn_name, sizeof(dn_name));
  153. #ifdef NSCACHE
  154.         cachedmn(key,rval,dn_name);
  155. #endif
  156.         }
  157.     }
  158.  
  159.     if (rval != OK)
  160.     {
  161. #ifdef DEBUG
  162.         if (rval == MAYBE)
  163.         ll_log(logptr, LLOGFTR, "nameserver query timed out");
  164.         else
  165.         ll_log(logptr, LLOGFTR, "nameserver query failed");
  166. #endif
  167.         return(rval);
  168.     }
  169.     }
  170.  
  171.     /* O.K. now give answer */
  172.  
  173.     switch (type)
  174.     {
  175.     case TB_CHANNEL:
  176.         /* if NS failure we returned MAYBE above */
  177.         if ((max_mxa <= 0) || (on_mxa >= max_mxa))
  178.         return(NOTOK);
  179.  
  180.         tmp = (char *)&(mx_addrs[on_mxa++]);
  181.  
  182.         (void) sprintf(value,"%u.%u.%u.%u",
  183.             ((unsigned)tmp[0]) & 0xff, ((unsigned)tmp[1]) & 0xff,
  184.             ((unsigned)tmp[2]) & 0xff, ((unsigned)tmp[3]) & 0xff);
  185.         break;
  186.  
  187.     default:
  188.         /* can't get multiple names */
  189.         if (!first)
  190.         return(NOTOK);
  191.  
  192.         /* give them the name */
  193.         (void) strcpy(value,dn_name);
  194.         break;
  195.     }
  196.     
  197. #ifdef  DEBUG
  198.     ll_log(logptr, LLOGFTR, "NS returns '%s'", value);
  199. #endif
  200.     return(OK);
  201. }
  202.  
  203. /*
  204.  * see if name is not cannonical name.
  205.  * We actually do an MX query and look for a CNAME RR.  By
  206.  * doing an MX query we ensure that the local BIND cache has
  207.  * been primed with MX's for later MX query (which may not be
  208.  * done in the same process).
  209.  */
  210.  
  211. LOCFUN
  212. ns_getcn(key, name, namelen)
  213. char *key, *name;
  214. int namelen;
  215. {
  216.     register int n;
  217.     register int count;
  218.     register char *cp;
  219.     u_short dsize, type;
  220.     char *eom;
  221.     union querybuf qbuf;
  222.     union ansbuf abuf;
  223.     HEADER *hp;
  224.     extern char *ns_skiphdr();
  225.  
  226. #ifdef DEBUG
  227.     ll_log(logptr, LLOGFTR, "ns_getcn(%s)",key);
  228. #endif
  229.  
  230.     /*
  231.      * We usually query for MX.  This should get us CNAME RRs if any
  232.      * and if no CNAME RRs, gets the MXs into local server's cache
  233.      * (a nice deal all around).  But there are certain cases in
  234.      * which aliases have MXs associated.  (This is wrong, but does
  235.      * happen).  In this case we may see problems.
  236.      */
  237.  
  238.     /* turn off resolver name-completion tricks */
  239. #ifdef RES_DNSRCH
  240.     _res.options &= ~((int) (RES_DEFNAMES | RES_DNSRCH));
  241. #else
  242.     _res.options &= ~((int) RES_DEFNAMES);
  243. #endif
  244.  
  245.  
  246. #ifdef ROBUST
  247.     n = res_mkquery(QUERY, key, C_IN, T_CNAME, (char *)0, 0, (char *)0,
  248.     (char *)&qbuf, sizeof(qbuf));
  249. #else
  250.     n = res_mkquery(QUERY, key, C_IN, T_MX, (char *)0, 0, (char *)0,
  251.     (char *)&qbuf, sizeof(qbuf));
  252. #endif
  253.  
  254.     /* what else can we do? */
  255.     if (n < 0) {
  256. #ifdef DEBUG
  257.     ll_log(logptr, LLOGFTR, "res_mkquery: n=%d, errno=%d, h_errno=%d", n, errno, h_errno);
  258. #endif
  259.     return(NOTOK);
  260.     }
  261.  
  262.     n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf));
  263.  
  264.     if (n < 0)
  265.     {
  266. #ifdef DEBUG
  267.     ll_log(logptr, LLOGFTR,
  268.         "ns_getcn: bad return from res_send, n=%d, errno=%d, h_errno=%d",
  269.         n, errno, h_errno);
  270. #endif
  271.     return(MAYBE);
  272.     }
  273.  
  274.     hp = (HEADER *)&abuf;
  275.  
  276.     if (hp->rcode != NOERROR)
  277.     return(ns_error(hp));
  278.  
  279.     if ((count=ntohs(hp->ancount)) == 0)
  280.     goto realname;
  281.  
  282.     /* only get here on NOERRR with ancount != 0 */
  283. #ifdef DEBUG
  284.     ll_log(logptr, LLOGFTR, "ns_getcn: parsing answer to query, hp->ancount=%d", count);
  285. #endif
  286.  
  287.     /* skip header */
  288.     eom = ((char *)&abuf)+n;
  289.  
  290.     if ((cp = ns_skiphdr((char *)&abuf, hp, eom)) == 0)
  291.     return(MAYBE);
  292.  
  293.     while ((cp < eom) && (count--))
  294.     {
  295.     if ((n = dn_expand((char *)&abuf,eom, cp, name, namelen))<0)
  296.         return(MAYBE);
  297.  
  298.     cp += n;
  299.     type = getshort(cp);
  300.  
  301.     /* skip to datasize */
  302.     cp += (2 * sizeof(u_short)) + sizeof(u_long);
  303.     dsize = getshort(cp);
  304.     cp += sizeof(u_short);
  305.  
  306.     if (type == T_CNAME)
  307.     {
  308.         if ((n = dn_expand((char *)&abuf, eom, cp, name, namelen))<0)
  309.         return(MAYBE);
  310.  
  311. #ifdef DEBUG
  312.         ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name);
  313. #endif
  314.         return(OK);
  315.     }
  316.  
  317.     /* skip to next RR */
  318.     cp += dsize;
  319.     }
  320.  
  321.     /*
  322.      * name is real name
  323.      */
  324.  
  325. realname:
  326.     (void) strncpy(name,key,namelen);
  327. #ifdef DEBUG
  328.     ll_log(logptr, LLOGFTR, "ns_getcn: %s -> %s",key,name);
  329. #endif
  330.     return(OK);
  331. }
  332.  
  333. /*
  334.  * build a list of addresses of MX hosts to try....
  335.  */
  336.  
  337. LOCFUN
  338. ns_getmx(key, max, mxtab, tsize)
  339. char *key;
  340. int *max;
  341. struct in_addr mxtab[];
  342. int tsize;
  343. {
  344.     register char *cp;
  345.     register int i, j, n;
  346.     HEADER *hp;
  347.     struct hostent *he;
  348.     union querybuf qbuf;
  349.     union ansbuf abuf;
  350.     u_short type, dsize;
  351.     int pref, localpref, tryagains;
  352.     int count, mxcount;
  353.     int sawmx;            /* are we actually processing mx's? */
  354.     char *eom;
  355.     char buf[MAXDNAME];        /* for expanding in dn_expand */
  356.     char newkey[MAXDNAME];     /* in case we get a CNAME RR back... */
  357.     struct {            /* intermediate table */
  358.     char *mxname;
  359.     u_short mxpref;
  360.     } mx_list[MAXMX];
  361.     extern char *ns_skiphdr();
  362.  
  363. #ifdef DEBUG
  364.     ll_log(logptr, LLOGFTR, "ns_getmx(%s, %x, %x, %d)",key,max,mxtab,tsize);
  365. #endif
  366.  
  367. restart:
  368.  
  369.     mxcount = 0;
  370.     *max = 0;
  371.     localpref = -1;
  372.     sawmx = 0;
  373.     tryagains = 0;
  374.  
  375.     /* turn off resolver name-completion tricks */
  376. #ifdef RES_DNSRCH
  377.     _res.options &= ~((int) (RES_DEFNAMES | RES_DNSRCH));
  378. #else
  379.     _res.options &= ~((int) RES_DEFNAMES);
  380. #endif
  381.  
  382.     n = res_mkquery(QUERY, key, C_IN, T_MX, (char *)0, 0, (char *)0,
  383.     (char *)&qbuf, sizeof(qbuf));
  384.  
  385.     /* what else can we do? */
  386.     if (n < 0) {
  387.     ll_log(logptr, LLOGFTR, "res_mkquery, n=%d, errno=%d, h_errno=%d", n, errno, h_errno);
  388.     return(NOTOK);
  389.     }
  390.  
  391. #ifdef DEBUG
  392.     ll_log(logptr, LLOGFTR, "ns_getmx: sending ns query (%d bytes)",n);
  393. #endif
  394.  
  395.     n = res_send((char *)&qbuf,n,(char *)&abuf, sizeof(abuf));
  396.  
  397.     if (n < 0) {
  398. #ifdef DEBUG
  399.     ll_log(logptr, LLOGFTR,
  400.         "ns_getmx: bad return from res_send, n=%d, errno=%d, h_errno=%d",
  401.         n, errno, h_errno);
  402. #endif
  403.     return(MAYBE);
  404.     }
  405.  
  406.     hp = (HEADER *)&abuf;
  407.  
  408. #ifdef OLDSERVER
  409.     if ((hp->rcode != NOERROR) && (hp->rcode != FORMERR))
  410. #else
  411.     if (hp->rcode != NOERROR) 
  412. #endif /* OLDSERVER */
  413.     return(ns_error(hp));
  414.  
  415. #ifdef OLDSERVER
  416.     if ((ntohs(hp->ancount) == 0) || (hp->rcode == FORMERR)) {
  417. #else
  418.     if (ntohs(hp->ancount) == 0) {
  419. #endif /* OLDSERVER */
  420.     mxcount = 1;
  421.     mx_list[0].mxname = strdup(key);
  422.     mx_list[0].mxpref = 0;
  423.     goto doaddr;
  424.     }
  425.  
  426.     /* read MX list */
  427.     sawmx = 1;
  428.     count = ntohs(hp->ancount);
  429.  
  430.     /* need local machine name */
  431.     if (local[0] == '\0') {
  432.     if ((locfullmachine != 0) && (*locfullmachine != '\0'))
  433.         (void) strcpy(local, locfullmachine);
  434.     else
  435.         (void) strcpy(local, locfullname);
  436.     }
  437.  
  438.     /* skip header */
  439.     eom = ((char *)&abuf) + n;
  440.     if ((cp = ns_skiphdr((char *)&abuf, hp, eom))==0)
  441.     goto quit;
  442.  
  443. #ifdef DEBUG
  444.     ll_log(logptr, LLOGFTR, "ns_getmx: %d answers to query",count);
  445. #endif
  446.  
  447.     while ((cp < eom) && (count--)) {
  448.     n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
  449.     if (n < 0)
  450.         goto quit;
  451.  
  452.     cp += n;
  453.     type = getshort(cp);
  454.     /* get to datasize */
  455.     cp += (2 * sizeof(u_short)) + sizeof(u_long);
  456.     dsize = getshort(cp);
  457.     cp += sizeof(u_short);
  458.  
  459.     /*
  460.      * is it an MX ? 
  461.      * note it could be a CNAME if we didn't use a domain lookup
  462.      */
  463.  
  464.     if (type == T_CNAME) {
  465.         ll_log(logptr,LLOGTMP,"ns_getmx: CNAME answer to MX query");
  466.         n = dn_expand((char *)&abuf,eom, cp, newkey, sizeof(newkey));
  467.         cp += dsize;
  468.         if (n < 0)
  469.         continue;    /* pray? */
  470. #ifdef DEBUG
  471.         ll_log(logptr,LLOGFTR,"ns_getmx: %s -> %s (new query)",key,newkey);
  472. #endif DEBUG
  473.         key = newkey;
  474.         goto restart;
  475.     }
  476.  
  477.     if (type != T_MX) {
  478.         ll_log(logptr,LLOGTMP,"ns_getmx: RR of type %d in response",type);
  479.         cp += dsize;
  480.         continue;       /* keep trying */
  481.     }
  482.  
  483.     pref = getshort(cp);
  484.     cp += sizeof(u_short);
  485.  
  486.     n = dn_expand((char *)&abuf,eom, cp, buf, sizeof(buf));
  487.     if (n < 0)
  488.         goto quit;
  489.  
  490.     cp += n;
  491.  
  492.     /* is it local? */
  493.     if ((lexequ(local,buf)) && ((localpref < 0) || (pref < localpref))) {
  494.         localpref = pref;
  495.         for(i=(mxcount-1); i >= 0; i--) {
  496.         if (mx_list[i].mxpref < localpref)
  497.             break;
  498.  
  499.         (void) free(mx_list[i].mxname);
  500.         mxcount--;
  501.         }
  502.         continue;
  503.     }
  504.  
  505.     /* now, see if we keep it */
  506.     if ((localpref >= 0) && (pref >= localpref))
  507.         continue;
  508.  
  509.     /*  find where it belongs */
  510.     for(i=0; i < mxcount; i++)
  511.         if (mx_list[i].mxpref > pref)
  512.         break;
  513.  
  514.     /* not of interest */
  515.     if (i == MAXMX)
  516.         continue;
  517.  
  518.     /* shift stuff to make space */
  519.     for(j=mxcount-1; j > i; j--) {
  520.         if (j==(MAXMX-1))
  521.         (void) free(mx_list[j].mxname);
  522.  
  523.         mx_list[j].mxname = mx_list[j-1].mxname;
  524.         mx_list[j].mxpref = mx_list[j-1].mxpref;
  525.     }
  526.  
  527.     mx_list[i].mxname = strdup(buf);
  528.     mx_list[i].mxpref = pref;
  529.  
  530.     if (mxcount <= i)
  531.         mxcount = i + 1;
  532.     }
  533.  
  534.     /*
  535.      * should read additional RR section for addresses and cache them
  536.      * but let's hold on that.
  537.      */
  538.  
  539. doaddr:
  540.     /* now build the address list */
  541. #ifdef DEBUG
  542.     ll_log(logptr, LLOGFTR,"ns_getmx: using %d mx hosts",mxcount);
  543. #endif
  544.  
  545.     for(i=0,j=0; (i < mxcount) && (j < tsize); i++) {
  546.     /*
  547.      * note that gethostbyname() is slow -- we should cache so
  548.      * we don't ask for an address repeatedly
  549.      */
  550.  
  551.     he = gethostbyname(mx_list[i].mxname);
  552.  
  553.     if (he == 0) {
  554. #ifdef DEBUG
  555.         ll_log(logptr, LLOGFTR, "ns_getmx: no addresses for %s",
  556.         mx_list[i].mxname);
  557. #endif
  558.         if (h_errno == TRY_AGAIN) {
  559.         tryagains++;
  560.         continue;
  561.         }
  562.  
  563.         /*
  564.          * were trying special case of no MXs and got no address
  565.          * name is clearly bad
  566.          */
  567.         if (!sawmx)
  568.         return(NOTOK);
  569.  
  570.         continue;
  571.     }
  572.  
  573.     for(n=0; (j < tsize) && (n < MAXADDR_PER); n++, j++) {
  574.         if (he->h_addr_list[n] == 0)
  575.         break;
  576.  
  577.         bcopy(he->h_addr_list[n],(char *)&mxtab[j],sizeof(struct in_addr));
  578.     }
  579. #ifdef DEBUG
  580.     ll_log(logptr, LLOGFTR, "ns_getmx: %d addresses saved for %s",
  581.         n, mx_list[i].mxname);
  582. #endif
  583.     }
  584.     *max = j;
  585.  
  586.     for(i=0; i < mxcount; i++)
  587.     (void) free(mx_list[i].mxname);
  588.  
  589.     /* decide which return value to give */
  590.  
  591.     /* if we have a list of addresses to try -> OK */
  592.     if (*max != 0)
  593.     return(OK);
  594.  
  595.     /* if we timed out on all the address lookups for MX's -> MAYBE */
  596.     if ((tryagains > 0) && (tryagains == mxcount))
  597.     return(MAYBE);
  598.  
  599.     /* lousy address */
  600.     return(NOTOK);
  601.  
  602. quit:                                  /* escape hatch for parsing problems */
  603.     for(i=0; i < mxcount; i++)
  604.     (void) free(mx_list[i].mxname);
  605.  
  606. #ifdef DEBUG
  607.     ll_log(logptr, LLOGFTR, "ns_getmx: problems parsing response to query");
  608. #endif
  609.  
  610.     return(MAYBE);
  611. }
  612.  
  613. /*
  614.  * figure out proper error code to return given an error
  615.  */
  616.  
  617. LOCFUN
  618. ns_error(hp)
  619. register HEADER *hp;
  620. {
  621.  
  622. #ifdef DEBUG
  623.     ll_log(logptr, LLOGFTR, "ns_error: server returned code %d",hp->rcode);
  624. #endif
  625.  
  626.     switch (hp->rcode)
  627.     {
  628.     case NXDOMAIN:
  629.         return(NOTOK); /* even if not authoritative */
  630.  
  631.     case SERVFAIL:
  632.         return(MAYBE);
  633.  
  634.     default:
  635.         break;
  636.     }
  637.  
  638.     return(NOTOK);
  639. }
  640.  
  641. /*
  642.  * skip header of query and return pointer to first answer RR.
  643.  */
  644.  
  645. LOCFUN
  646. char *ns_skiphdr(answer, hp, eom)
  647. char *answer;
  648. HEADER *hp;
  649. register char *eom;
  650. {
  651.     register int qdcount;
  652.     register char *cp;
  653.     register int n;
  654.     char tmp[MAXDNAME];
  655.  
  656.     qdcount = ntohs(hp->qdcount);
  657.  
  658.     cp = answer + sizeof(HEADER);
  659.  
  660.     while ((qdcount-- > 0) && (cp < eom))
  661.     {
  662.     n = dn_expand(answer,eom,cp,tmp,sizeof(tmp));
  663.     if (n < 0)
  664.         return(0);
  665.     cp += (n + QFIXEDSZ);
  666.     }
  667.  
  668.     return((cp < eom)? cp : 0);
  669. }
  670.  
  671. /*
  672.  * routine to set the resolver timeouts
  673.  * takes maximum number of seconds you are willing to wait
  674.  */
  675.  
  676. ns_settimeo(ns_time)
  677. int     ns_time;
  678. {
  679.     static int called = 0;
  680.     static struct state oldres;
  681.  
  682.     if ((_res.options & RES_INIT) == 0)
  683.         res_init ();
  684.  
  685.     /* always start afresh */
  686.     if (called)
  687.     {
  688.     bcopy((char *)&oldres,(char *)&_res,sizeof(oldres));
  689.     }
  690.     else
  691.     {
  692.     called = 1;
  693.     bcopy((char *)&_res,(char *)&oldres,sizeof(oldres));
  694.     }
  695.  
  696.     /*
  697.      * bind uses an exponential backoff
  698.      */
  699.  
  700.     _res.retrans = ns_time >> _res.retry;
  701.  
  702. #ifdef DEBUG
  703.     ll_log(logptr, LLOGFTR, "ns_timeo: servers(%d), retrans(%d), retry(%d)",
  704.     _res.nscount, _res.retrans, _res.retry);
  705. #endif
  706. }
  707.  
  708. /*
  709.  * Caching stuff starts here....
  710.  * cache stores following pairs:
  711.  *        key -> mx address list
  712.  *        key -> canonical name
  713.  *
  714.  * also stores rvals for keys, so if we failed or timedout, we only
  715.  * do that once in the process lifetime.
  716.  *
  717.  * must store complete answers to a table fetch  -- storing by RR's
  718.  * leads to incomplete information in the cache -- and thus busted
  719.  * routing.
  720.  */
  721.  
  722. /*
  723.  * see if key/type pair is in cache.  If so, set rval appropriately
  724.  */
  725. #ifdef NSCACHE
  726.  
  727. static cachehash(key)
  728. register char *key;
  729. {
  730.     register int i;
  731.     register unsigned c;
  732.     register unsigned sum;
  733.     extern char chrcnv[];
  734.  
  735.     sum = 0;
  736.  
  737.     for(i=0; *key != '\0'; i++, key++)
  738.     {
  739.     c = chrcnv[*key & 0x7f] & 0xff;
  740.     sum +=  c* i;
  741.     }
  742.  
  743.     return(sum % NSCACHE);
  744. }
  745.  
  746. cachehit(key,rval,tbltype)
  747. char *key;
  748. int *rval;
  749. int tbltype;
  750. {
  751.     register int i;
  752.     register struct ns_cache *cp;
  753.     register struct in_addr *ip1, *ip2;
  754.  
  755.     *rval = OK;
  756.  
  757.     i = cachehash(key);
  758.  
  759.     ll_log(logptr,LLOGFTR,"ns: key %s -> %d\n",key,i);
  760.  
  761.     if (tbltype == TB_CHANNEL)
  762.     cp = chncache[i];
  763.     else if ((tbltype == TB_DOMAIN))
  764.     cp = dmncache[i];
  765.     else
  766.     {
  767.     ll_log(logptr,LLOGFTR,"ns: no cache!\n");
  768.     cp = 0;
  769.     }
  770.  
  771.     for(; cp != 0; cp = cp->nc_next)
  772.     {
  773.     if (!lexequ(key,cp->nc_key))
  774.         continue;
  775.  
  776. #ifdef DEBUG
  777.     ll_log(logptr,LLOGFTR,"ns: cache hit, key %s\n",key);
  778. #endif /* DEBUG */
  779.  
  780.     if ((*rval = cp->nc_rval) == OK)
  781.     {
  782.         switch (tbltype)
  783.         {
  784.         case TB_CHANNEL:
  785.             /* fill mx cache */
  786.             max_mxa = cp->nc_count;
  787.             on_mxa = 0;
  788.  
  789.             ip1 = (struct in_addr *)cp->nc_data;
  790.             ip2 = mx_addrs;
  791.             for(i=0; i < max_mxa; i++)
  792.             *ip2++ = *ip1++;
  793.             break;
  794.  
  795.         case TB_DOMAIN:
  796.             /* fill in dn_name */
  797.             if (cp->nc_count)
  798.             (void) strcpy(dn_name,cp->nc_data);
  799.             else
  800.             (void) strcpy(dn_name,cp->nc_key);
  801.         }
  802.     }
  803.  
  804.     return(1);
  805.     }
  806.  
  807.     /* no one should look at rval, but... */
  808.     *rval = NOTOK;
  809.     return(0);
  810. }
  811.  
  812.  
  813. cachechn(key,rval,count,list)
  814. char *key;
  815. int rval, count;
  816. struct in_addr *list;
  817. {
  818.     register struct ns_cache *cp;
  819.     register struct in_addr *ip1, *ip2;
  820.     int i;
  821.     extern char *malloc(), *calloc();
  822.  
  823.     /* NOSTRICT */
  824.     if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0)
  825.     return;
  826.  
  827.     /* NOSTRICT */
  828.     if ((cp->nc_data = calloc((unsigned)count,sizeof(*list)))==0)
  829.     {
  830.     (void) free((char *)cp);
  831.     return;
  832.     }
  833.     if ((cp->nc_key = strdup(key))==0)
  834.     {
  835.     (void) free(cp->nc_data);
  836.     (void) free((char *)cp);
  837.     return;
  838.     }
  839.  
  840.     cp->nc_count = count;
  841.     cp->nc_rval = rval;
  842.  
  843.     ip1 = list;
  844.     ip2 = (struct in_addr *)cp->nc_data;
  845.  
  846.     while (count--)
  847.     *ip2++ = *ip1++;
  848.  
  849.     /* stuff it in */
  850.     i = cachehash(cp->nc_key);
  851.  
  852.     cp->nc_next = chncache[i];
  853.     chncache[i] = cp;
  854. }
  855.  
  856. cachedmn(key,rval,name)
  857. char *key, *name;
  858. int rval;
  859. {
  860.     register struct ns_cache *cp;
  861.     int i;
  862.     extern char *malloc(), *strdup();
  863.  
  864.     /* NOSTRICT */
  865.     if ((cp = (struct ns_cache *)malloc(sizeof(*cp)))==0)
  866.     return;
  867.  
  868.     if ((cp->nc_key = strdup(key))==0)
  869.     {
  870.     (void) free((char *)cp);
  871.     return;
  872.     }
  873.  
  874.     if ((name != 0) && !lexequ(key,name))
  875.     {
  876.     if ((cp->nc_data = strdup(key)) == 0)
  877.     {
  878.         (void) free(cp->nc_key);
  879.         (void) free((char *)cp);
  880.         return;
  881.     }
  882.     cp->nc_count = 1;
  883.     }
  884.     else
  885.     {
  886.     cp->nc_data = 0;
  887.     cp->nc_count = 0;
  888.     }
  889.  
  890.     cp->nc_rval = rval;
  891.  
  892.     /* stuff it in */
  893.     i = cachehash(cp->nc_key);
  894.  
  895.     cp->nc_next = dmncache[i];
  896.     dmncache[i] = cp;
  897. }
  898. #endif NSCACHE
  899.